#pragma once

#include "../Math/Vec2i.h"
#include "../Math/Vec3f.h"
#include <libtcod\libtcod.hpp>
#include <memory>
#include <vector>

typedef std::shared_ptr<TCODColor> TCODColorPtr;
typedef std::shared_ptr<TCODMap> TCODMapPtr;
typedef std::shared_ptr<TCODImage> TCODImagePtr;

class DeferredRenderer
{
public:
	DeferredRenderer(const Vec2i &viewport_size);
	~DeferredRenderer();

	void prepare();
	void render();

	enum BlendType {
		BLEND_NONE,
		BLEND_SET,
		BLEND_MULTIPLY,
		BLEND_LIGHTEN,
		BLEND_DARKEN,
		BLEND_SCREEN,
		BLEND_COLOR_DODGE,
		BLEND_COLOR_BURN,
		BLEND_ADD,
		//BLEND_ADDA,
		BLEND_BURN,
		BLEND_OVERLAY,
		//BLEND_ALPH,
		BLEND_DEFAULT
	};
	enum RenderTarget {
		RT_DIFFUSE,
		RT_NORMAL,
		RT_SPECULAR,
		RT_DEPTH,
		RT_SHININESS,
		RENDER_TARGET_COUNT
	};
	enum RenderLayer {
		RL_BACKGROUND,
		RL_FOREGROUND,
		RENDER_LAYER_COUNT
	};

	void shade(const Vec2i &pos, const Vec3f &data, RenderLayer layer, RenderTarget target, BlendType blending = BLEND_DEFAULT);
	void shade(const Vec2i &pos, float data, RenderTarget target);

	void light(const Vec2i &pos, float h, const TCODMapPtr &fov, const Vec3f &color, int radius, BlendType blending = BLEND_DEFAULT, bool isPlayerLightSource = false);
	void setAmbient(const Vec3f &background, const Vec3f &foreground) { ambient.bkgr = background; ambient.frgr = foreground; }

	void addSymbol(const Vec2i &pos, uint8 character, float weight = 1.0f, bool hide_if_hidden = true);

	void setMapInfo(const Vec2i &pos, bool transparent, bool walkable);

private:
	Vec2i viewport_size;
	Vec2i renderTarget_size;
	struct RenderData
	{
		Vec3f bkgr;
		Vec3f frgr;
		RenderData() : bkgr(), frgr() {}
	};
	std::vector<RenderData> diffuses;
	std::vector<RenderData> normals;
	std::vector<RenderData> speculars;
	std::vector<float> depths;
	std::vector<float> shininess;

	TCODImagePtr renderTarget;

	struct LightData
	{
		Vec3f pos;
		TCODMapPtr light_fov;
		Vec3f color;
		int radius;
		BlendType blending;
		bool isPlayerLightSource;
		LightData() : pos(), light_fov(nullptr), color(), radius(0), blending(BLEND_DEFAULT), isPlayerLightSource(false) {}
	};
	std::vector<LightData> lights;

	struct LightMapData
	{
		Vec3f attenuationColor;
		std::vector<float> NdotL;
		std::vector<float> term;
		LightMapData() : attenuationColor() {}
	};
	std::vector<LightMapData> lightMap;

	struct MapData
	{
		bool transparent;
		bool walkable;
		MapData() : transparent(false), walkable(false) {}
	};
	std::vector<MapData> map_info;

	struct Symbol
	{
		uint8 character;
		float weight;
		bool hide_if_hidden;
		Symbol() : character(' '), weight(-1.0f), hide_if_hidden(true) {}
	};
	std::vector<Symbol> symbols;
	RenderData ambient;

	void computeLightMap(std::vector<LightData*> &lightPos);
	enum TermType
	{
		TERM_PHONG,
		TERM_BLINN,
		TERM_GAUSS,
		TERM_TYPE_COUNT
	};
	float computeTerm(TermType term, const Vec3f &N, const Vec3f &L, const Vec3f &viewDirection, float NdotL, float shininess);
	bool toViewSpace(const Vec2i &in_worldspace, Vec2i &out_viewspace, int &index);
};
typedef std::shared_ptr<DeferredRenderer> DeferredRendererPtr;
